#include "ind_arbol_clas.h"


IndArbolClas::IndArbolClas(std::string& nombre, unsigned int tam_bloque, unsigned int tam_bloque_indice,
		 unsigned int num_campo_clave,TiposArchivo tipo_indice):IndiceSec(nombre, tam_bloque,tam_bloque_indice,num_campo_clave){

	//supongo que SIEMPRE ME VA A LLEGAR BIEN EL TIPO_INDICE
	
	std::string nombre_arbol = nombre + ".tree";
	if (tipo_indice == INDICE_B)
		arbol = new ArbolB(nombre_arbol,tam_bloque); 
	if (tipo_indice == INDICE_B_MAS)
		arbol = new ArbolBMas(nombre,tam_bloque);
	
	this->tipo = tipo_indice;
		
	TipoInt * tipo_base = new TipoInt();
	arbol->set_tipo_base(*tipo_base);
	arbol->agregar_observador(Buffer::get_instance());
	delete tipo_base;
	std::string nombre_overflow = nombre + ".oflw";
	arch_overflow = new ArchOverflow(nombre_overflow, tam_bloque);
	arch_overflow->agregar_observador(Buffer::get_instance());
}

IndArbolClas::~IndArbolClas(){
	if (arbol != NULL)
		delete arbol;
	arbol = NULL;
	if (arch_overflow !=NULL)
		delete arch_overflow;
	arch_overflow = NULL;
}

void IndArbolClas::crear(std::string formato) throw(ExcepCreandoArch, ExcepSintaxisParser, ExcepBloqueTam){
	(Debug::get_instance() )->mostrar_mensaje("<INDICE SECUNDARIO> Se crea el indice secundario. ");
	if (formato.compare("[int,int]") != 0)
		throw ExcepSintaxisParser("Error - Se esparaba tipo int para el formato del indice. ");
	arbol->crear();
	std::string formato_oflow = "[int]";
	arch_overflow->crear(formato_oflow);
}
		
void IndArbolClas::abrir() throw(ExcepAbriendoArch){
	(Debug::get_instance() )->mostrar_mensaje("<INDICE SECUNDARIO> Se crea el indice secundario. ");
	arbol->abrir();
	arch_overflow->abrir();
}

void IndArbolClas::cerrar(){
	(Debug::get_instance() )->mostrar_mensaje("<INDICE SECUNDARIO> Se cierra el indice secundario. ");
	arbol->cerrar();
	arch_overflow->cerrar();
}

void IndArbolClas::eliminar(){
	(Debug::get_instance() )->mostrar_mensaje("<INDICE SECUNDARIO> Se elimina el indice secundario. ");
	arbol->eliminar();
	arch_overflow->eliminar();
}

TipoStruct* IndArbolClas::generar_reg_oflow(int dato){
	//Creo un nuevo registro del tipo base de oflow y lo lleno con los datos
	TipoStruct* reg_retorno = (TipoStruct*)arch_overflow->get_registro_base()->clonar();
	(*reg_retorno)[0] = TipoInt(dato);
	return reg_retorno;	
}

void IndArbolClas::ingresar(TipoStruct* reg) throw(ExcepArchCerrado,ExcepRegExistente, ExcepBloqueTam){
	//if (this->fd == -1)
		//throw ExcepArchCerrado("Error - No se puede ingresar el registro el archivo esta cerrado");
	(Debug::get_instance() )->mostrar_mensaje("<INDICE SECUNDARIO> Se ingresa una clave en el indice secundario. ");
	TipoDato* clave = &((*reg)[get_numero_campo()]);
	TipoInt * res; 
	if (this->tipo == INDICE_B)
		res = arbol->consultar(clave);
	if (this->tipo == INDICE_B_MAS)
		res = ((ArbolBMas*)arbol)->consultar_exacto(clave);
	int campo_cero = ((TipoInt*)(&((*reg)[0])))->get_valor();
	TipoStruct* reg_oflow = generar_reg_oflow(campo_cero);
	int nbloque = -1;	
	
	if (res != NULL){ //ya existe en el arbol,agrego a la cadena de overflow 
		nbloque = res->get_valor();
		arch_overflow->ingresar(nbloque, reg_oflow);
		delete res;
	}else{
		//pido un bloque libre en el arch de overflow, lugar donde voy a apuntar desde el arbol	
		nbloque = arch_overflow->get_primer_bloque_libre();	
		//lo meto en el arbol
		TipoInt * num = new TipoInt(nbloque);
		arbol->ingresar(clave->clonar(),(TipoInt *)num->clonar());
		delete num;
		//agrego en el arch de overflow la referencia al campo 0 del reg
		arch_overflow->ingresar(nbloque, reg_oflow);
	
	}		
	delete reg_oflow;
}

void IndArbolClas::quitar(TipoStruct* reg) throw(ExcepArchCerrado){
	//if (this->fd == -1)
		//throw ExcepArchCerrado("Error - No se puede ingresar el registro el archivo esta cerrado");
	(Debug::get_instance() )->mostrar_mensaje("<INDICE SECUNDARIO> Se quita una clave del indice secundario ");
	int nbloque = -1;
	int resultado;	
	int campo_cero = ((TipoInt*)(&((*reg)[0])))->get_valor();
	TipoStruct* reg_oflow = generar_reg_oflow(campo_cero);
	TipoDato* clave = &((*reg)[get_numero_campo()]);
	TipoInt* res;
	if (this->tipo == INDICE_B)
		res = arbol->consultar(clave);
	if (this->tipo == INDICE_B_MAS)
		res = ((ArbolBMas*)arbol)->consultar_exacto(clave);

	if (res != NULL){
		nbloque = res->get_valor(); 
		resultado = arch_overflow->quitar(nbloque,reg_oflow);
		if (resultado == SIN_ELEMENTOS)
				arbol->quitar(clave);
		else {
			if (resultado != SIN_CAMBIOS) { 
				//es decir cambio la ref desde el arbol al bloque del arch oflow
				//le paso la clave que ya se encuentra en el arbol y la nueva referencia
				//arbol->actualizar(clave,resultado);
			}
		}
		delete res;
	}
	// Sino no hago nada
	delete reg_oflow;
}


TipoDato* IndArbolClas::generar_clave_arbol(std::vector<ParesDatos*> &consultas){
	TipoDato* clave = NULL;
	std::vector<ParesDatos*>::iterator it = consultas.begin();
	//Recorro las consultas. Si una es por campo clave del secundario
	//creo un reg base del arbol y lo lleno con el dato q se esta consultando
	while ((it != consultas.end()) && (clave == NULL)) {
		ParesDatos* par = (*it);	
		if (par->get_numero_campo() == this->num_campo_clave) {
			clave = par->get_valor()->clonar();
		}
		it++;
	}
	return clave;
}


bool IndArbolClas::consultar(std::vector<ParesDatos*> &consultas, std::vector<TipoStruct*> &resultado)
									 throw(ExcepArchCerrado,ExcepLeerBloque){
	(Debug::get_instance() )->mostrar_mensaje("<INDICE SECUNDARIO> Se efectua una consulta de clasificacion. ");
	bool correcta = true;
	int nbloque = -1;
	resultado.clear();
	TipoDato* clave = generar_clave_arbol(consultas);
	if (clave != NULL) {
		TipoInt * res;
		if (this->tipo == INDICE_B)
			res = arbol->consultar(clave);
		if (this->tipo == INDICE_B_MAS)
			res = ((ArbolBMas*)arbol)->consultar_exacto(clave);
		if (res != NULL){
			nbloque = res->get_valor(); 
			arch_overflow->consultar_todos(nbloque, resultado);
			delete res;
		}
	} else
		correcta = false;
	
	delete(clave);
	return correcta;
}

unsigned int IndArbolClas::get_espacio_libre()  throw(ExcepArchCerrado, ExcepLeerBloque){
	unsigned int resultado = arbol->get_espacio_libre() + arch_overflow->get_espacio_libre();
	return resultado;
}
									 
unsigned int IndArbolClas::get_cant_regs() throw(ExcepArchCerrado){
	unsigned int resultado = arbol->get_cant_regs() + arch_overflow->get_cant_regs();
	return resultado;
}

long IndArbolClas::get_tam() throw(ExcepArchCerrado){
	unsigned int resultado = arbol->get_tam() + arch_overflow->get_tam();
	return resultado;
}
